#!/usr/bin/python

'''
Created on Feb 1, 2011

@author: ziegeldorf
'''

import os, glob, math

class Sensors:

    def __init__(self, header):
        '''
            Pass the header performance.h header (resp the file that contains the
            enum perf_sensor in it) to this constructor.
            The constructor then parses the available sensors from that file.
        '''
        self.s = []
        self.comments = {}
        try:
            f = open(header)
        except:
            print "Could not open header at: " + header
            return
        for line in f:
            if line.strip() == "enum perf_sensor {":
                break
        for line in f:
            if line.strip() == "PERF_MAX":
                #print "found end: " + line
                break
            elif line.find("*") == -1 and len(line.strip()) > 0:
                parts = line.split(",")
                sensor = parts[0].strip()
                comment = ",".join(parts[1:]).strip()
                self.s.append(sensor)
                self.comments[sensor] = comment
            #else:
                #print "skipping " + line
                
        additional = []
        #additional.append(["START", "// Start ..."])
        #additional.append(["PERF_PING", "// ICMP Ping from initiator to responder"])
        #additional.append(["PERF_BLACKBOX", "// Blackbox test on connection establishment"])
        #additional.append(["PERF_SEND_CERT_CHAIN_R", "// time at responder to send cert chain"])
        #additional.append(["PERF_CERT_UP_CERT_ACK_R", "// time at responder to receive cert ack"])
        #additional.append(["PERF_SEND_CERT_CHAIN_I", "// time at initiator to send cert chain"])
        #additional.append(["PERF_CERT_UP_CERT_ACK_I", "// time at initiator to receive cert ack"])
        #additional.append(["PERF_HANDLE_CERT_CHAIN_I", "// time at initiator to handle cert chain"])
        #additional.append(["PERF_HANDLE_CERT_CHAIN_R", "// time at initiator to handle cert chain"])
        additional.append(["PERF_I1", "// time at responder to handle I1"])
        additional.append(["PERF_I2", "// time at responder to handle I2"])
        additional.append(["PERF_R1", "// time at initiator to handle R1"])
        additional.append(["PERF_R2", "// time at initiator to handle R2"])
        #additional.append(["PERF_CONN_U1_R", "// time at responder to handle first udpate of a new connection"])
        #additional.append(["PERF_CONN_U2_I", "// time at initiator to handle second udpate of a new connection"])
        #additional.append(["PERF_CONN_U3_R", "// time at responder to handle third udpate of a new connection"])
        #additional.append(["PERF_CONN_U3_R", "// time at responder to handle third udpate of a new connection"])
        #additional.append(["PERF_CONN_U3_R", "// time at responder to handle third udpate of a new connection"])

	additional.append(["PERF_HASH_I", ""])
        additional.append(["PERF_HASH_R", ""])
        additional.append(["PERF_X509AC_VERIFY_CERT_CHAIN_I", ""])
        additional.append(["PERF_X509AC_VERIFY_CERT_CHAIN_R", ""])
        additional.append(["PERF_CONN_REQUEST_I", ""])
        additional.append(["PERF_CONN_REQUEST_R", ""])
        additional.append(["PERF_IP6TABLES_I", ""])
        additional.append(["PERF_IP6TABLES_R", ""])
        for item in additional:
            self.s.append(item[0])
            self.comments[item[0]] = item[1]

        self.s = sorted(self.s)

    def index(self, name):
        try:
            i = self.s.index(name)
        except:
            i = -1
        return i
    
    def __iter__(self):
        return self.s.__iter__()
    
    def printSensorList(self, comments=1):
        width=5
        rows = len(self.s) / width + 1
        
        if comments:
            for i in range(len(self.s)):
                print " %02d: %s   %s" %(i+1, self.s[i].ljust(30), self.comments[self.s[i]])
            return
        
        for i in range(rows):
            for j in range(width):
                if j*rows +i < len(self.s):
                    print " %02d: %s " % ((j*rows + i + 1), self.s[j*rows + i].ljust(30)),
            print


class PerformanceSet:
    def __init__(self, measurement_directory, header="/home/ashish/Thesis/mbox-services/lib/core/performance.h"):
        self.sensors = Sensors(header)
        print " The following sensors are available"
        print " ---------------------------------------------------------------------------- "
        self.sensors.printSensorList()
        print " ---------------------------------------------------------------------------- "
        self.measurements = {}
        for sensor in self.sensors:
            self.measurements[sensor] = {}
        self.read_measurements(measurement_directory, "*")
        
    def clear(self):
        self.measurements = {}
        for sensor in self.sensors:
            self.measurements[sensor] = {}
        
    def read_measurements(self, dir, pattern):
        print "Reading measurments from all files in  " + dir + " that match pattern " + pattern
        for infile in glob.glob( os.path.join(dir, pattern) ):
            sensor = infile.split('/')[-1].split('.')[0]
            index = self.sensors.index(sensor)
            if index == -1:
                print "Skipping file: " + infile + "(no entry in sensor list)"
                continue

            #print "Reading measurments from file: " + infile.split('/')[-1] + \
            #      " for sensor " + sensor + "(" + str(index) +")"
            
            f = open(infile, 'r')
            for line in f:
                if sensor == "PERF_PING":
                    key = 0
                    val = float(line.strip())/1000
                else:
                    split = line.split()
                    key = split[0]
                    val = split[1]
                try:
                    self.measurements[sensor][key].append(val)
                except:
                    self.measurements[sensor][key] = [val]
            f.close()

    def printSensors(self):
        print "------------------------------- SENSORS -------------------------------"
        for key in sorted(self.measurements):
            self.printSensor(key)
        print "-------------------------------   END   -------------------------------"

    def printSensor(self, sensor, prefix="  ", format="avg", ignore_empty=1):
        try:
            m = self.measurements[sensor]
        except:
            print "error: Invalid sensor: " + sensor
        if not ignore_empty or len(m) > 0:
            avg = self.average(sensor)
            if format == "all":
                print prefix + "" + sensor + " measurements: " + str(avg)
            else:
                print prefix + '%(sens)s \t  average:     avg: %(avg).5f    var: %(var).7f    std: %(std).7f  (%(count)02d measurements)   %(alert)s' % {"sens":sensor.ljust(30), "avg": avg['all'][0], "var": avg['all'][1], "std": avg['all'][2], "count" : avg['all'][3], "alert": avg['all'][4]}
                if len(avg) > 2:
                    for key in sorted(avg):
                        if key != "all":
                            print prefix + '%(fill)s  \t  point %(subsens)02d:  avg: %(avg).5f    var: %(var).7f    std: %(std).7f  (%(count)02d measurements)   %(alert)s' % {"fill" : " ".ljust(30), "subsens":int(key), "avg": avg[key][0], "var": avg[key][1], "std": avg[key][2], "count" : avg[key][3], "alert": avg[key][4]} 

    def averageList(self):
        ret = {}
        for sensor in self.measurements:
            avg = self.average(sensor)
            try:
                ret[sensor] = avg["all"][0]
                if len(avg) > 2:
                    for key in avg:
                        if key != "all":
                            ret[sensor+"_"+key] = avg[key][0]
            except:
                print "Could not get averages for sensor " + sensor + "... skipping!"
        return ret
    
    def devList(self):
        ret = {}
        for sensor in self.measurements:
            avg = self.average(sensor)
            try:
                ret[sensor] = avg["all"][2]
                if len(avg) > 2:
                    for key in avg:
                        if key != "all":
                            ret[sensor+"_"+key] = avg[key][2]
            except:
                print "Could not get standard dev for sensor " + sensor + "... skipping!"
        return ret
    
    def varList(self):
        ret = {}
        for sensor in self.measurements:
            avg = self.average(sensor)
            try:
                ret[sensor] = avg["all"][3]
                if len(avg) > 2:
                    for key in avg:
                        if key != "all":
                            ret[sensor+"_"+key] = avg[key][3]
            except:
                print "Could not get standard dev for sensor " + sensor + "... skipping!"
        return ret

    def variance(self, values):
        if len(values) <= 0:
            return [0.0, 0, 0, 0, "EMPTY"]
        
        sum = 0.0
        sum_quad = 0.0
        for x in map(float, values):
            sum += x
            sum_quad += x**2
        mean = sum / len(values)
        variance = sum_quad/len(values) - (sum/len(values))**2
        stddev = math.sqrt(variance)
        return [mean, variance, stddev, len(values), ""]
         

    def average(self, sensor):
        try:
            m = self.measurements[sensor]
        except:
            print "error: Invalid sensor: " + sensor
            return {}

        avg = {}
        all_values = []
        for key in m:
            avg[key] = self.variance(m[key])
            all_values.extend(m[key])
            
        avg["all"] = self.variance(all_values)
        
        for key in avg:
            if avg[key][2] > avg[key][0]*0.2:
                avg[key][4] = "ALERT: Standard deviation"
        
        return avg
